/*
 * Copyright (c) 2025 Li Auto Inc. and its affiliates
 * Licensed under the Apache License, Version 2.0(the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>
#include <stdlib.h>
#include <ctype.h>
#include <signal.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <sys/ioctl.h>
#include <linux/sockios.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>

#include "eth_general_types.h"
#include "veth_internal.h"
#include "veth.h"
#include "sim_port_adapt.h"
#include "sim_tapdev_posix.h"
#include "sim_tapdev.h"
#include "veth_adapter.h"

#define NETWORK_BUFFER_LEN (1522)

tap_dev_type tap_dev[vETH_MAX_CONTROLLERS];
sim_port_sem_t tap_send_sem = NULL_PTR;

static Std_ReturnType tap_create_worker_threads(void);
static void *linuxtap_send_thread(void *pvParam);
static void *linuxtap_recv_thread(void *pvParam);
static boolean is_valid_frame(const uint8 ctrl_idx, uint8 *pucBuffer);

Std_ReturnType tap_init(veth_core_ctrl_config_type *ctrl_cfg_ptr, veth_ctrl_status_type *ctrl_status_ptr,
            txdone_cbk_ptr txdone_cbk_func, rxdone_cbk_ptr rxdone_cbk_func)
{
    int fd;
    char tap_name[IFNAMSIZ];
    uint8 ctrl_idx;
    int len;

    ctrl_idx = ctrl_cfg_ptr->eth_ctrl_idx;
    len = snprintf(tap_name, IFNAMSIZ, "%s", CONFIG_SIM_TAP_NAME);
    if (len >= IFNAMSIZ) {
        veth_print(LOG_DEBUG, "[tap] tap device name too long\n");
        return E_NOT_OK;
    }

    if (0 != tap_create(tap_name, &fd)) {
        return E_NOT_OK;
    }

    ctrl_cfg_ptr->dev_fd = fd;

    tap_dev[ctrl_idx].devfd = fd;
    tap_dev[ctrl_idx].rx_buf_total = ctrl_cfg_ptr->eth_rx_buf_total;
    tap_dev[ctrl_idx].tx_buf_total = ctrl_cfg_ptr->eth_tx_buf_total;
    tap_dev[ctrl_idx].rx_buf_len_byte = ctrl_cfg_ptr->eth_ctrl_rx_buf_len_byte;
    tap_dev[ctrl_idx].tx_buf_len_byte = ctrl_cfg_ptr->eth_ctrl_tx_buf_len_byte;
    tap_dev[ctrl_idx].ctrl_status_ptr = ctrl_status_ptr;
    tap_dev[ctrl_idx].txdone_cbk_func = txdone_cbk_func;
    tap_dev[ctrl_idx].rxdone_cbk_func = rxdone_cbk_func;
    tap_dev[ctrl_idx].curr_rx_buf_idx = 0;

    if (E_OK != tap_create_worker_threads()) { /* Create tap device send/receive thread */
        return E_NOT_OK;
    }
    return E_OK;
}

Std_ReturnType tap_get_controller_mode(const uint8 ctrl_idx, eth_mode_type *ctrl_mode)
{
    int fd;
    int cur_ctrl_mode;

    fd = tap_dev[ctrl_idx].devfd;
    if (0 != tap_get_mode(fd, &cur_ctrl_mode)) {
        veth_print(LOG_DEBUG, "tap get mode failed.\n");
        return E_NOT_OK;
    }
    *ctrl_mode = (eth_mode_type)cur_ctrl_mode;
    return E_OK;
}

Std_ReturnType tap_set_controller_mode(const uint8 ctrl_idx, eth_mode_type const ctrl_mode)
{
    int fd;

    fd = tap_dev[ctrl_idx].devfd;
    if (0 != tap_set_mode(fd, ctrl_mode)) {
        veth_print(LOG_DEBUG, "tap set mode failed.\n");
        return E_NOT_OK;
    } else {
        return E_OK;
    }
}

static Std_ReturnType tap_create_worker_threads(void)
{
    pthread_t tap_recv_thread, tap_send_thread;
    Std_ReturnType Ret = E_NOT_OK;

    if (NULL == tap_send_sem) {
        if (SIM_PORT_SUCCESS != sim_port_sem_init(&tap_send_sem, 0)) { /* Create semaphore */
            veth_print(LOG_DEBUG, "[tap] Failed to create TAP send event\n");
            return E_NOT_OK;
        }

        if (0 != pthread_create(&tap_recv_thread, NULL, linuxtap_recv_thread, NULL)) {
            veth_print(LOG_DEBUG, "[tap] Rx thread create failed\n");
            sim_port_sem_destroy(tap_send_sem);
            tap_send_sem = NULL;
            return E_NOT_OK;
        }

        if (0 != pthread_create(&tap_send_thread, NULL, linuxtap_send_thread, NULL)) {
            veth_print(LOG_DEBUG, "[tap] tx thread create failed\n");
            pthread_cancel(tap_recv_thread);
            sim_port_sem_destroy(tap_send_sem);
            tap_send_sem = NULL;
            return E_NOT_OK;
        }

        pthread_setname_np(tap_recv_thread, "TAP_Rx");
        pthread_setname_np(tap_send_thread, "TAP_Tx");
        veth_print(LOG_DEBUG, "[tap] tx rx thread create success\n");
        Ret = E_OK;
    }

    return Ret;
}

static void *linuxtap_send_thread(void *pv_param)
{
    uint8 ctrlid;
    uint8 bufid;
    int tapfd;
    uint16 tx_buf_length_config;
    ssize_t bytes_written;
    veth_ctrl_status_type *ctrl_status_ptr = NULL;
    veth_tx_buf_tables_type *curr_tx_buff_table_ptr = NULL;
    uint8 *tx_buffer_address = NULL;
    uint8 *tx_buff_base_ptr = NULL;

    for (;;) {
        if (SIM_PORT_SUCCESS != sim_port_sem_wait(tap_send_sem)) {
            veth_print(LOG_DEBUG, "tap_send_thread: wait failed!\n");
            continue;
        }
        for (ctrlid = 0; ctrlid < vETH_MAX_CONTROLLERS; ctrlid++) {
            tapfd = tap_dev[ctrlid].devfd;
            ctrl_status_ptr = tap_dev[ctrlid].ctrl_status_ptr;
            if ((NULL == ctrl_status_ptr) ||
                (ETH_MODE_DOWN == ctrl_status_ptr->eth_run_time_opertn_data.eth_controller_mode)) {
                continue;
            }
            tx_buff_base_ptr = &(ctrl_status_ptr->eth_buff_ptr->eth_tx_buffer_ptr[0]);
            tx_buf_length_config = tap_dev[ctrlid].tx_buf_len_byte;
            for (bufid = 0; bufid < tap_dev[ctrlid].tx_buf_total; bufid++) {
                curr_tx_buff_table_ptr = &(ctrl_status_ptr->eth_buff_ptr->eth_tx_buff_table_ptr[bufid]);
                if ((1 == curr_tx_buff_table_ptr->buff_status) &&
                    (1 == curr_tx_buff_table_ptr->frame_tx_initiated) &&
                    (1 != curr_tx_buff_table_ptr->frame_tx_sent)) {
                    tx_buffer_address = tx_buff_base_ptr + (uint32)bufid * tx_buf_length_config;
                    veth_print(LOG_DEBUG, "[tap] txbufid is %d\n", bufid);
                    veth_print(LOG_DEBUG, "[tap] tx_length is %d\n", curr_tx_buff_table_ptr->length);
                    bytes_written = tap_write(tapfd, tx_buffer_address, curr_tx_buff_table_ptr->length);
                    if (bytes_written < 0) {
                        veth_print(LOG_DEBUG, "[tap] write error\n");
                        continue;
                    } else if (bytes_written != curr_tx_buff_table_ptr->length) {
                        veth_print(LOG_DEBUG, "[tap] partial write: %zd/%d bytes\n", bytes_written,
                            curr_tx_buff_table_ptr->length);
                    }

                    curr_tx_buff_table_ptr->frame_tx_sent = 1;

                    if (NULL != tap_dev[ctrlid].txdone_cbk_func) {
                        tap_dev[ctrlid].txdone_cbk_func(ctrlid);
                    }
                }
            }
        }
    }
    return NULL;
}

static boolean is_valid_frame(const uint8 ctrl_idx, uint8 *puc_buffer)
{
    uint8 broadcast_mac[vETH_MACADDRESS_LEN];
    const uint8 *local_mac = NULL;

    memset(broadcast_mac, 0xff, vETH_MACADDRESS_LEN);
    local_mac = tap_dev[ctrl_idx].ctrl_status_ptr->eth_run_time_opertn_data.eth_unicast_mac_address;
    return ((memcmp(puc_buffer, local_mac, vETH_MACADDRESS_LEN) == 0) ||  /* Unicast */
        (memcmp(puc_buffer, broadcast_mac, vETH_MACADDRESS_LEN) == 0) ||  /* Broadcast */
        ((puc_buffer[0] & 0x01) == 1));                                  /* Multicast */
}

static void *linuxtap_recv_thread(void *pv_param)
{
    uint8 uc_frame[NETWORK_BUFFER_LEN];
    uint8 ctrlid;
    uint8 bufid;
    int tapfd;
    boolean buffer_avail;
    uint16 rx_buf_length_config;
    ssize_t size_received;
    veth_ctrl_status_type *ctrl_status_ptr = NULL;
    veth_rx_buf_tables_type *curr_rx_buff_table_ptr = NULL;
    uint8 *rx_buffer_address = NULL;
    uint8 *rx_buff_base_ptr = NULL;

    for (;;) {
        usleep(1000);
        for (ctrlid = 0; ctrlid < vETH_MAX_CONTROLLERS; ctrlid++) {
            ctrl_status_ptr = tap_dev[ctrlid].ctrl_status_ptr;
            if ((NULL == ctrl_status_ptr) ||
                (ETH_MODE_DOWN == ctrl_status_ptr->eth_run_time_opertn_data.eth_controller_mode)) {
                continue;
            }
            tapfd = tap_dev[ctrlid].devfd;
            size_received = tap_read(tapfd, uc_frame, sizeof(uc_frame));
            if (size_received > vETH_FRAMEHEADER_LENGTH) {
                veth_print(LOG_DEBUG, "[tap] recieve size %ld\n", size_received);

                buffer_avail = FALSE;
                rx_buff_base_ptr = &(ctrl_status_ptr->eth_buff_ptr->eth_rx_buffer_ptr[0]);
                rx_buf_length_config = tap_dev[ctrlid].rx_buf_len_byte;
                for (bufid = 0; bufid < tap_dev[ctrlid].rx_buf_total; bufid++) {
                    curr_rx_buff_table_ptr =
                        &(ctrl_status_ptr->eth_buff_ptr->eth_rx_buff_table_ptr[tap_dev[ctrlid].curr_rx_buf_idx]);
                    if (0 == curr_rx_buff_table_ptr->buff_status) {
                        buffer_avail = TRUE;
                        curr_rx_buff_table_ptr->buff_status = 1;
                        veth_print(LOG_DEBUG, "[tap] new used rxbuf is %d\n", tap_dev[ctrlid].curr_rx_buf_idx);
                        rx_buffer_address = rx_buff_base_ptr + (uint32)bufid * rx_buf_length_config;
                        memcpy(rx_buffer_address, uc_frame, size_received);
                        curr_rx_buff_table_ptr->length = (uint16)size_received;
                        curr_rx_buff_table_ptr->frame_rx_initiated = 1;
                        if (NULL != tap_dev[ctrlid].rxdone_cbk_func) {
                            tap_dev[ctrlid].rxdone_cbk_func(ctrlid);
                        }

                        tap_dev[ctrlid].curr_rx_buf_idx = (tap_dev[ctrlid].curr_rx_buf_idx + 1U) %
                                        (tap_dev[ctrlid].rx_buf_total);
                        break;
                    }
                }
                if (FALSE == buffer_avail) {
                    veth_print(LOG_DEBUG, "[tap] tap receive get rx buffer failed.\n");
                    sleep(1);
                }
            }
        }
    }
    return NULL;
}
